/**
    MVP Tree data structure using graph database for I/O storage
    Copyright (C) 2014  David G. Starkweather

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    David Starkweather starkd88@gmail.com
**/

package org.phash.mvptree;

import java.lang.StrictMath;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;

/** 
 * Datapoint class for representing units of
 * data as byte[].
 **/
public class RHDatapoint extends Datapoint {

    public RHDatapoint(Node node){
	super(node);
    }

    public RHDatapoint(Node node, byte[] data){

	super(node);
	setData(data);

    }

    RHDatapoint(Datapoint point){
	super(point);
    }


    public void setData(byte[] data) {
	Node node = getNode();
	Transaction tx = node.getGraphDatabase().beginTx();
	try {
	    node.setProperty(DATA, data);
	    tx.success();
	}finally {
	    tx.finish();
	}
    }

    public byte[] getData() {
	Node node = getNode();
	Transaction tx = node.getGraphDatabase().beginTx();
	byte[] data = null;
	try {
	    data = (byte[])node.getProperty(DATA);
	    tx.success();
	} finally {
	    tx.finish();
	}
	return data;
    }

    /**
     * compute L1 distance betweeen two points 
     *
     * @param point other Datapoint with which to compare
     * @return float val for distance between two points 
     *
     **/
    public float distance(Datapoint point){
	Datapoint.incrementNumOps();

	if (point == null)
	    throw new BadDistanceValueException("null");

	RHDatapoint otherPoint = (RHDatapoint)point;
	byte[] x = getData();
	byte[] y = otherPoint.getData();

	if (x == null || y == null || x.length != y.length) 
	    throw new BadDistanceValueException();

	int n = x.length;

	// calc mean of both series
	double mx = 0, my = 0;
	for (int i=0;i<n;i++){
	    mx += (double)x[i];
	    my += (double)y[i];
	}
	mx /= (double)n;
	my /= (double)n;

	// calculate normalization factors
	double sx = 0, sy = 0;
	for (int i=0;i<n;i++){
	    sx += ((double)x[i] - mx) * ((double)x[i] - mx);
	    sy += ((double)y[i] - my) * ((double)y[i] - my);
	} 
	double denom = Math.sqrt(sx*sy);

	// calculate correlation series, save maximum.
	double peak = 0;
	for (int d = 0;d < n;d++){
	    double sxy = 0;
	    for (int i=0;i<n;i++){
		int j = (i + d)%n;
		sxy += ((double)x[i] - mx)*((double)y[j] - my);
	    }
	    sxy /= denom;
	    if (sxy  > peak) peak = sxy;
	}

	double result = 1.0 - peak;
	return (float)result;
    }

}
